from . import api
from ihome.models import Area, House, Facility,User,Order
from flask import current_app,jsonify,g,request,session
from ihome.utils.response_code import RET
from ihome import redis_store, db
import json
from ihome import constance
from datetime import datetime

# 获取房源列表
# GET http://127.0.0.1:5000/api/v1.0/houses?sd=2017-12-01&ed=2017-12-31&aid=10&sk=new&p=1
@api.route("/houses")
def get_house_list():
    """获取房屋列表信息(搜索页面)"""
    start_date = request.args.get("sd","") # 获取想要的起始时间
    end_date = request.args.get("ed","") # 获取想要的结束时间
    area_id = request.args.get("aid","") # 区域编号
    sort_key = request.args.get("sk","new") # 排序关键字
    page = request.args.get("p") # 页号

    # 处理时间
    try:
        if start_date:
            # strptime():将str转化成time
            # strftime():将time转化成str
            start_date = datetime.strptime(start_date, "%Y-%m-%d")
        if end_date:
            end_date = datetime.strptime(end_date, "%Y-%m-%d")
        if start_date and end_date:
            assert start_date <= end_date
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg="日期参数错误")

    # 判断区域id
    if area_id:
        try:
            area = Area.query.get(area_id)
        except Exception as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.PARAMERR, errmsg="区域参数有误")
    # 处理页号
    try:
        page = int(page)
    except Exception as e:
        current_app.logger.error(e)
        page = 1

    # 获取缓存数据
    redis_key = "house_%s_%s_%s_%s" % (start_date, end_date, area_id, sort_key)
    try:
        resp_json = redis_store.hget(redis_key, page)
    except Exception as e:
        current_app.logger.error(e)
    else:
        if resp_json:
            print("从redis缓存中提取的数据")
            return resp_json, 200, {"Content-Type": "application/json"}

    # 过滤条件的参数列表容器
    filter_params = []
    # 填充过滤条件
    conflict_orders = None
    try:
        if start_date and end_date:
            # 查询冲突的订单
            conflict_orders = Order.query.filter(Order.begin_date <= end_date, Order.end_date >= start_date).all()
        elif start_date:
            conflict_orders = Order.query.filter(Order.end_date >= start_date).all()
        elif end_date:
            conflict_orders = Order.query.filter(Order.begin_date <= end_date).all()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="从Mysql数据库中查询有冲突的订单发送异常")

    if conflict_orders:
        # 从订单中获取冲突的房屋id
        conflict_house_ids = [order.house_id for order in conflict_orders]
        # 如果冲突的订单id不为空，向查询参数中追加条件
        if conflict_house_ids:
            # select * from ih_house_info where house_id not in (有冲突房子列值)
            filter_params.append(House.id.notin_(conflict_house_ids))

    # 区域条件
    if area_id:
        # filter_params=[House.area_id == area_id]
        filter_params.append(House.area_id == area_id)

    # 查询数据库
    # 补充排序条件
    if sort_key == "booking":  # 入住最多
        house_query = House.query.filter(*filter_params).order_by(House.order_count.desc())
    elif sort_key == "price-inc":
        house_query = House.query.filter(*filter_params).order_by(House.price.asc())
    elif sort_key == "price-des":
        house_query = House.query.filter(*filter_params).order_by(House.price.desc())
    else:
        # 新旧排序
        house_query = House.query.filter(*filter_params).order_by(House.create_time.desc())

    # 处理分页
    try:
        # 得到分页器对象                                   当前页数  每页数据量                                  自动的错误输出
        paginate = house_query.paginate(page=page, per_page=constance.HOUSE_LIST_PAGE_CAPACITY, error_out=False)
    except Exception as e:
        current_app.logger.error(e)
        print("e=", e)
        return jsonify(errno=RET.DBERR, errmsg="根据页码和每页记录数获取分页器对象时发生异常")
    # 获取页面数据
    house_li = paginate.items # 当前页的所有数据
    print("从Mysql数据库中获取了数据")
    houses = []
    for house in house_li:
        houses.append(house.to_basic_dict())
    # 获取总页数
    total_page = paginate.pages
    # {"errno":"0","errmsg":"OK",data:{"total_page":4,"houses":[{house信息对象},{}],"current_page":1}}
    # return jsonify(errno=RET.OK, errmsg="OK", data={"total_page": total_page, "houses": houses, "current_page": page})

    # 房屋列表在redis缓存中的处理：redis_store
    # 使用字符串存储格式："house_起始_结束__区域id_排序_页数"
    # setex house_起始_结束__区域id_排序_1"  有效期  json字符串
    # setex house_起始_结束__区域id_排序_2"  有效期  json字符串
    # setex house_起始_结束__区域id_排序_3"  有效期  json字符串
    # 使用这种方式的问题是：如果第一页数据失效，此时查询条件稍微有点变化，第二页第三页数据都应该失效，
    # 否则造成数据错误，因此这种设计不合理。
    # 使用hash存储：
    # hset "house_起始_结束__区域id_排序":
    # {
    #   "1":{},
    #   "2":{},
    # }
    # 这种格式比较合理，可以一次清除所有页数据
    resp_dict = dict(errno=RET.OK, errmsg="OK", data={"total_page": total_page, "houses": houses,
                                                      "current_page": page})
    resp_json = json.dumps(resp_dict, ensure_ascii=False)
    if page <= total_page:  # 只有当前页小于等于总页数时数据才有效
        # 设置缓存数据redis的key: "house_%s_%s_%s_%s"%(start_date,end_date,area_id,sort_key)
        redis_key = "house_%s_%s_%s_%s" % (start_date, end_date, area_id, sort_key)
        try:
            # 无法保证所有代码都执行成功
            # redis_store.hset(redis_key, page, resp_json)
            # redis_store.expire(redis_key, constance.HOUSE_LIST_PAGE_REDIS_CACHE_EXPIRES)

            # 如果出现第一行设置成功，第二行设置失败就成了缓存永久有效，不合理
            # 创建redis管道对象，可以一次执行多条语句，保证管道中的指令要么都成功，要么都失败
            pipeline = redis_store.pipeline()
            # 开启多个语句的记录
            pipeline.multi()
            pipeline.hset(redis_key, page, resp_json)
            pipeline.expire(redis_key, constance.HOUSE_LIST_PAGE_REDIS_CACHE_EXPIRES)
            # 执行语句
            pipeline.execute()
            print("将从mysql数据库中查询出来的数据缓存到redis数据库中")
        except Exception as e:
            current_app.logger.error(e)
    return resp_json, 200, {"Content-Type": "application/json"}


# GET http://127.0.0.1:5000/api/v1.0/areas
@api.route("/areas")
def get_area_info():
    """查询所有城区信息"""
    try:
        # 先从redis数据库中获取数据
        redis_bytes = redis_store.get("area_info")
    except Exception as e:
        current_app.logger.error(e)
    else:
        if redis_bytes is not None:
            print("从redis缓存中获取的数据")
            area_info= redis_bytes.decode("utf-8")
            return area_info,200,{"Content-Type":"application/json"}

    try:
        area_li = Area.query.all()
        print("从Mysql数据库中查询到城区信息")
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR,errmsg="数据库异常")

    area_dict_li = []
    for area in area_li:
        area_dict_li.append(area.to_dict())

    # 使用json模块将python字典转化成json字符串
    resp_dict = dict(errno=RET.OK,errmsg="OK",data=area_dict_li)
    resp_json=json.dumps(resp_dict,ensure_ascii=False)

    try:
        redis_store.setex("area_info",constance.AREA_INFO_REDIS_CACHE_EXPIRES,resp_json)
    except Exception as e:
        current_app.logger.error(e)
    # 以元组的形式返回响应结果，(响应体，响应码，响应头)
    return resp_json,200,{"Content-Type":"application/json"}

from ihome.utils.common import login_required
# POST http://127.0.0.1:5000/api/v1.0/houses/info
@api.route("/houses/info",methods=["POST"])
@login_required
def save_house_info():
    """
    保存房屋基本信息,前端传递过来的数据格式为json字符串，
    前端发送过来的json数据
    {
        "title":"",
        "price":"",
        "area_id":"1",
        "address":"",
        "room_count":"",
        "acreage":"",
        "unit":"",
        "capacity":"",
        "beds":"",
        "deposit":"",
        "min_days":"",
        "max_days":"",
        "facility":["7","8"]
    }
    """
    user_id = g.user_id
    # 1. 接收前端传递过来的数据
    house_data = request.json
    title = house_data.get("title")  # 房屋名称的标题
    price = house_data.get("price")  # 房屋价格
    area_id = house_data.get("area_id")  # 房屋所属区域编号
    address = house_data.get("address")  # 房屋地址
    room_count = house_data.get("room_count")  # 房屋包含的房间数目
    acreage = house_data.get("acreage")  # 房屋面积
    unit = house_data.get("unit")  # 房屋布局（几室几厅)
    capacity = house_data.get("capacity")  # 房屋容纳人数
    beds = house_data.get("beds")  # 房屋卧床数目
    deposit = house_data.get("deposit")  # 押金
    min_days = house_data.get("min_days")  # 最小入住天数
    max_days = house_data.get("max_days")  # 最大入住天数

    # 2. 校验参数
    # 校验参数
    if not all([title, price, area_id, address, room_count, acreage, unit, capacity, beds, deposit, min_days, max_days]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数不完整")

    # 判断金额是否正确
    try:
        # 将价格和押金转化为分，默认传递过来的是元，所以需要乘以100
        price = int(float(price) * 100)
        deposit = int(float(deposit) * 100)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg="参数错误")

    # 3. 执行业务逻辑
    # 判断城区id是否存在
    try:
        area = Area.query.get(area_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="数据库异常")

    if area is None:
        return jsonify(errno=RET.NODATA, errmsg="城区信息有误")

    # 保存房屋信息
    house = House(
        user_id=user_id,
        area_id=area_id,
        title=title,
        price=price,
        address=address,
        room_count=room_count,
        acreage=acreage,
        unit=unit,
        capacity=capacity,
        beds=beds,
        deposit=deposit,
        min_days=min_days,
        max_days=max_days
    )

    # 处理房屋的设施信息
    facility_ids = house_data.get("facility")
    # 如果用户勾选了设施信息，再保存数据库
    if facility_ids:  # ["7","8"]
        try:
            # select  * from ih_facility_info where id in []
            facilities = Facility.query.filter(Facility.id.in_(facility_ids)).all()
        except Exception as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR, errmsg="数据库异常")

        if facilities:
            # 表示有合法的设施数据
            # 保存设施数据
            house.facilities = facilities
    try:
        db.session.add(house)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg="保存数据失败")

    # 4. 保存数据成功,返回响应结果
    # {"errno":"0","errmsg":"OK","data":{"house_id":1}}
    return jsonify(errno=RET.OK, errmsg="OK", data={"house_id": house.id})


# GET http://127.0.0.1:5000/api/v1.0/houses/1
@api.route("/houses/<int:house_id>",methods=["GET"])
def get_house_detail(house_id):
    """
    获取房屋详情
    :param house_id:  房屋的id值
    :return: 房屋详情
    """
    # 前端在展示房屋详情页面时，如果浏览页面的用户不是当前房屋的房东，则显示【即刻预定】按钮，否则不显示
    # 所以需要返回后端登录的user_id
    # 尝试获取用户的登录信息，若登录，返回给前端登录的user_id,若未登录，则返回-1
    user_id = session.get("user_id",-1)
    # 校验参数
    if not house_id:
        return jsonify(errno=RET.PARAMERR,errmsg="参数错误")

    try:
        # 先从redis缓存中获取信息: house_info_1
        ret = redis_store.get("house_info_%s"%house_id)
    except Exception as e:
        current_app.logger.error(e)
        ret = None

    if ret:
        current_app.logger.info("从redis数据库中获取的缓存信息")
        print("从redis数据库中获取的缓存信息")
        return '{"errno":"0", "errmsg":"OK", "data":{"user_id":%s,"house":%s}}' % (user_id, ret.decode("utf-8")),200, \
               {"Content-Type": "application/json"}
    try:
        print("从Mysql数据库中查询房子信息")
        # 根据house_id查询House类型的对象
        house = House.query.get(house_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR,errmsg="查询数据库失败")
    if not house:
        return jsonify(errno=RET.NODATA, errmsg="房屋id为%s的访问不存在"%house_id)
    try:
        # 将房屋对象转换为字典
        house_data = house.to_full_dict()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR,errmsg="数据出错")
    json_house = json.dumps(house_data,ensure_ascii=False)
    try:
        # house_info_1
        redis_store.setex("house_info_%s"%house_id,constance. HOUSE_DETAIL_REDIS_EXPIRE_SECOND,json_house)
    except Exception as e:
        current_app.logger.error(e)
    return '{"errno":"0", "errmsg":"OK", "data":{"user_id":%s,"house":%s}}' % (user_id, json_house),200, \
           {"Content-Type": "application/json"}




from ihome.utils.image_storage import storage
from ihome.models import HouseImage
# POST http://127.0.0.1:5000/api/v1.0/houses/image
@api.route("/houses/image", methods=["POST"])
@login_required
def save_house_image():
    """保存房屋的图片
    参数 图片 房屋的id
    """
    image_file = request.files.get("house_image")
    house_id = request.form.get("house_id")

    if not all([image_file, house_id]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数错误")

    # 判断house_id正确性
    try:
        house = House.query.get(house_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="数据库异常")

    if house is None:  # if not house:
        return jsonify(errno=RET.NODATA, errmsg="房屋不存在")

    image_data = image_file.read()
    # 保存图片到七牛中
    try:
        file_name = storage(image_data)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.THIRDERR, errmsg="保存图片失败")
    # 保存图片信息到数据库中
    house_image = HouseImage(house_id=house_id, url=file_name)
    db.session.add(house_image)
    # 处理房屋的主图片,如果房屋主图片为空则设置图片为当前图片的文件名
    if not house.index_image_url:
        house.index_image_url = file_name
        db.session.add(house)
    try:
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg="保存图片数据异常")
    image_url = constance.QINIU_URL_DOMAIN + file_name
    # {"errno":"0","errmsg":"OK","data":{"image_url":"图片的完整路径"}}
    return jsonify(errno=RET.OK, errmsg="OK", data={"image_url": image_url})

# GET http://127.0.0.1:5000/api/v1.0/user/houses
@api.route("/user/houses",methods=["GET"])
@login_required
def get_user_houses():
    """获取房东发布的房源信息条目"""
    user_id = g.user_id
    try:
        user = User.query.get(user_id)
        houses = user.houses
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="获取数据失败")

    # 将查询到的房屋信息转换为字典存放到列表中
    houses_list = []
    if houses:
        for house in houses:
            houses_list.append(house.to_basic_dict())
    return jsonify(errno=RET.OK, errmsg="OK", data={"houses": houses_list})

# GET http://127.0.0.1:5000/api/v1.0/houses/index
@api.route("/houses/index", methods=["GET"])
def get_house_index():
    """获取主页幻灯片展示的房屋基本信息"""
    # 从缓存中尝试获取数据
    try:
        ret = redis_store.get("home_page_data")
    except Exception as e:
        current_app.logger.error(e)
        ret = None

    if ret:
        current_app.logger.info("当前数据从redis缓存中获取成功")
        print("当前数据从redis缓存中获取成功")
        # 因为redis中保存的是json字符串，所以直接进行字符串拼接返回
        # 注意：由于从redis取出来的数据是bytes类型，必须解码成字符串在返回，否则前端js不响应，数据无法显示
        return '{"errno":0, "errmsg":"OK", "data":%s}' % ret.decode("utf-8"), 200,{"Content-Type": "application/json"}
    else:
        print("===从mysql数据库中获取主页幻灯片数据==")
        try:
            # 查询数据库，返回房屋订单数目最多的5条数据
            houses = House.query.order_by(House.order_count.desc()).limit(constance.HOME_PAGE_MAX_HOUSES)
        except Exception as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR, errmsg="查询数据失败")

        if not houses:
            return jsonify(errno=RET.NODATA, errmsg="查询无数据")
        houses_list = []
        for house in houses:
            # 如果房屋未设置主图片，则跳过
            if not house.index_image_url:
                continue
            houses_list.append(house.to_basic_dict())
        # 将数据转换为json，并保存到redis缓存
        json_houses = json.dumps(houses_list,ensure_ascii=False)  # "[{},{},{}]"
        try:
            redis_store.setex("home_page_data", constance.HOME_PAGE_DATA_REDIS_EXPIRES, json_houses)
        except Exception as e:
            current_app.logger.error(e)
        return '{"errno":0, "errmsg":"OK", "data":%s}' % json_houses, 200, {"Content-Type": "application/json"}










